<?php

// error_reporting(E_ALL & ~E_WARNING & ~E_NOTICE & ~E_DEPRECATED);

require realpath(dirname(__FILE__)) . '/../vendor/autoload.php';

use EventServer\Connection;

$config = require 'config.php';

$host = '0.0.0.0';
$port = (int) getServerArgv(1, 8080);
$server = new swoole_websocket_server($host, $port);

$redisConfig = isset($config['redis']) ? $config['redis'] : ['host' => '127.0.0.1', 'port' => 6379];
$redis = new Redis();
$redis->pconnect($redisConfig['host'], $redisConfig['port']);
$redis->delete(Connection::MAP_KEY);

$server->on("open", function(swoole_websocket_server $server, $request) use($config) {
    writeLog('open ' . $request->fd);
    $headerData = $request->header;
    $serverData = $request->server;
    if (isset($headerData['origin'])) {
        if (!in_array($headerData['origin'], $config['allowDomains'])) {
            $server->close($request->fd);
            writeLog('domain deny: ' . $headerData['origin']);
        }
    } else if (isset($serverData['remote_addr'])) {
        if (!in_array($serverData['remote_addr'], $config['allowHosts'])) {
            $server->close($request->fd);
            writeLog('server deny: ' . $serverData['remote_addr']);
        }
    } else {
        $server->close($request->fd);
        writeLog('bad request: no origin and remote_addr');
    }
});

$server->on("close", function(swoole_websocket_server $server, $fd) use($redis) {
    $connection = new Connection($redis, $server, $fd);
    if(($key = $connection->close()) && $key != LOG_SESSIONID) {
        writeLog(sprintf('connection closed, key: %s, fd: %d', $key, $fd));
    }
});

$server->on("message", function(swoole_websocket_server $server, $frame) use($redis) {
    writeLog('receive: ' . $frame->data);

    if (!($event = parseEvent($frame))) {
        return false;
    }
    
    $connection = new Connection($redis, $server, $frame->fd);

    switch ($event['name']) {
        case 'register':
            if(isset($event['sessionid'])) {
                $connection->register($event['sessionid']);
            }
            break;
        case 'notify':
            if(isset($event['sessionid']) && checkSign($event) && isset($event['data'])) {
                $connection->notify($event['sessionid'], $event['data']);
            }
            break;
        case 'subscribe':
            if(isset($event['channel'])) {
                $connection->subscribe($event['channel']);
            }
            break;
        case 'publish':
            if(isset($event['channel']) && checkSign($event) && isset($event['data'])) {
                $connection->publish($event['channel'], $event['data']);
            }
            break;
        case 'clear':
            if(checkSign($event)) {
                $connection->clear();
            }
            break;
    }
});

$server->on('start', function(\swoole_websocket_server $server) use($host, $port) {
    writeLog(sprintf('webscoket server start at: %s:%d, master pid: %d', $host, $port, $server->master_pid));
});

$server->start();

/* * * * * * * * * * * * * * * * * * * functions * * * * * * * * * * * * * * * * * * * * * * * * * * */

/**
 * 获取命令行参数
 * @param type $index
 * @param type $default
 */
function getServerArgv($index, $default = null)
{
    $server = $_SERVER;
    if(isset($server['argv']) && isset($server['argv'][$index])) {
        return $server['argv'][$index];
    }
    return $default;
}

/**
 * 解析事件数据
 * @param type $frame
 * @return boolean
 */
function parseEvent($frame)
{
    if (!($event = json_decode($frame->data, true)) || !isset($event['name'])) {
        return false;
    }
    
    return $event;
}

/**
 * 验证签名
 * @param type $event
 * @return boolean
 */
function checkSign($event)
{
    if(!isset($event['name']) || !isset($event['timestamp']) || !isset($event['sign'])) {
        return FALSE;
    }
    return strcmp($event['sign'], md5($event['name'] . $event['timestamp'] . SIGN_SECRET)) === 0;
}

/**
 * 调试
 * @param type $content
 * @return boolean
 */
function writeLog($content) {
    global $server, $redis;
    
    if(defined('DEBUG') && DEBUG) {
        return printf("%s\n", $content);
    }
    
    if($redis->hExists(Connection::MAP_KEY, LOG_SESSIONID)) {
        $fd = $redis->hGet(Connection::MAP_KEY, LOG_SESSIONID);
        if($server->exist($fd)) {
            $message = json_encode(array(
                'name' => 'log',
                'data' => $content
            ));
            
            return $server->push($fd, $message);
        }
        
        $redis->hDel(Connection::MAP_KEY, LOG_SESSIONID);
    }
}